local VERSION = tonumber(("$Revision: 218 $"):match("%d+"))

local _G = getfenv(0)

local deformat = AceLibrary("Deformat-2.0")

local L = AceLibrary("AceLocale-2.2"):new("AuldLangSyne_Note")

local AuldLangSyne = AuldLangSyne
local Note
if AuldLangSyne then
	Note = AuldLangSyne:NewModule("Note", "AceEvent-2.0", "AceHook-2.1", "AceConsole-2.0")
	if AuldLangSyne.revision < VERSION then
		AuldLangSyne.version = "r" .. VERSION
		AuldLangSyne.revision = VERSION
		AuldLangSyne.date = ("$Date: 2009-01-21 16:42:40 +0000 (Wed, 21 Jan 2009) $"):match("%d%d%d%d%-%d%d%-%d%d")
	end
else
	Note = AceLibrary("AceAddon-2.0"):new("AceEvent-2.0", "AceHook-2.1", "AceConsole-2.0", "AceDB-2.0")
	AuldLangSyne_Note = Note -- since this isn't a module in this branch, global for access
end

function Note:OnInitialize(name)
	local realmdefaults = {
		friend = {},
		ignore = {},
		guild = {},
		generic = {},
	}
	local profiledefaults = {
		inmenu = true,
		intooltip = true,
		onlogon = true,
		onwho = true,
	}
	if AuldLangSyne then
		self.db = AuldLangSyne:AcquireDBNamespace("Note")
		AuldLangSyne:RegisterDefaults("Note", "realm", realmdefaults)
		AuldLangSyne:RegisterDefaults("Note", "profile", profiledefaults)
	else
		self:RegisterDB("AuldLangSyneNoteDB")
		self:RegisterDefaults("realm", realmdefaults)
		self:RegisterDefaults("profile", profiledefaults)
	end
	
	self.menu = {
		handler = Note,
		name = "Notes", type = "group",
		desc = "Player note options",
		args = {
			inmenu = {
				name = L["Add notes to player menus"], type = "toggle",
				desc = L["Adds an 'edit note' entry to the right-click menu for players"],
				get = function() return self.db.profile.inmenu end,
				set = function(t)
					self.db.profile.inmenu = t
					if t then
						self:AddMenuItems()
					else
						self:RemoveMenuItems()
					end
				end,
			},
			intooltip = {
				name = L["Note in player tooltip"], type = "toggle",
				desc = L["Turns display of player notes in tooltips on and off."],
				get = function() return self.db.profile.intooltip end,
				set = function(t) self.db.profile.intooltip = t end,
			},
			onlogon = {
				name = L["Note on logon"], type = "toggle",
				desc = L["Turns display of player notes on logon on and off."],
				get = function() return self.db.profile.onlogon end,
				set = function(t) self.db.profile.onlogon = t end,
			},
			onwho = {
				name = L["Note on /who"], type = "toggle",
				desc = L["Turns display of player notes when you /who them on and off."],
				get = function() return self.db.profile.onwho end,
				set = function(t) self.db.profile.onwho = t end,
			},
			ctimport = {
				name = L["ctimport"], type = "execute",
				desc = L["Import notes for this realm from CT_PlayerNotes"],
				func = "CTImport",
				order = 99,
			},
		}
	}
	
	self:RegisterChatCommand({'/fnote',}, {
		name = "Note", type = "text",
		desc = "Add a note to a player",
		usage = "/fnote <player name> <note text>",
		get = false,
		set = function(s)
			if s and #s > 0 then
				local name, note = string.match(s, "^(%a+) *(.*)")
				name = string.gsub(name, "^(%l)", string.upper, 1) --Normalize the name.
				if #note > 0 then
					self.db.realm.generic[name] = note
					self:Print(L["Set note for %s: %s"]:format(name, note))
				else
					self:PrintNoteFor(name)
				end
			end
		end,
	})
	self:RegisterChatCommand({'/fnotedel'}, {
		name = "Remove Note", type = "text",
		desc = "Remove a note from a player",
		usage = "/fnotedel <player name>",
		get = false,
		set = function(s)
			if s and #s > 0 then
				local name, note = string.match(s, "^(%a+) *(.*)")
				name = string.gsub(name, "^(%l)", string.upper, 1) --Normalize the name.
				if self.db.realm.generic[name] then
					self:Print(L["Removed note from %s.  (Was: %s)"]:format(name, self.db.realm.generic[name]))
					self.db.realm.generic[name] = nil
				end
			end
		end,
	})
	
	if AuldLangSyne then
		if AuldLangSyne.db.realm.chars then
			--Pre-modular data is here.  Import it.
			for name, data in pairs(AuldLangSyne.db.realm.chars) do
				if data.note then
					self.db.realm.friend[name] = data.note
					data.note = nil
				end
			end
		end
		if AuldLangSyne.db.realm.ignore then
			self.db.realm.ignore = AuldLangSyne.db.realm.ignore
			AuldLangSyne.db.realm.ignore = nil
		end
		if AuldLangSyne.db.realm.guild then
			self.db.realm.guild = AuldLangSyne.db.realm.guild
			AuldLangSyne.db.realm.guild = nil
		end
		if AuldLangSyne.db.realm.generic then
			self.db.realm.generic = AuldLangSyne.db.realm.generic
			AuldLangSyne.db.realm.generic = nil
		end
	end
end

function Note:OnEnable(first)
	if first then
		-- We will need many buttons.
		self.friendbuttons, self.ignorebuttons, self.guildbuttons = {}, {}, {}
		for i=1, FRIENDS_TO_DISPLAY, 1 do
			table.insert(self.friendbuttons, self:CreateButton("AuldLangSyne_FriendNote"..i, _G["FriendsFrameFriendButton"..i], "RIGHT", "RIGHT", -5, 0))
			self.friendbuttons[i].type = "friend"
		end
		for i=1, IGNORES_TO_DISPLAY, 1 do
			table.insert(self.ignorebuttons, self:CreateButton("AuldLangSyne_IgnoreNote"..i, _G["FriendsFrameIgnoreButton"..i], "RIGHT", "RIGHT", -5, 0))
			self.ignorebuttons[i].type = "ignore"
		end
		for i=1, GUILDMEMBERS_TO_DISPLAY, 1 do
			table.insert(self.guildbuttons, self:CreateButton("AuldLangSyne_GuildNote"..i, _G["GuildFrameButton"..i], "RIGHT", "RIGHT", 0, 0))
			self.guildbuttons[i].type = "guild"
		end
		self.editFrame = self:CreateEditFrame()
	else
		for i,t in pairs(self.friendbuttons) do
			t:Show()
		end
		for i,t in pairs(self.ignorebuttons) do
			t:Show()
		end
		for i,t in pairs(self.guildbuttons) do
			t:Show()
		end
	end
	
	self:SecureHook("FriendsList_Update")
	self:SecureHook("IgnoreList_Update")
	self:SecureHook("GuildStatus_Update")
	self:SecureHook("SendWho", function(filter) self:ListenFor("Who") end)
	self:HookScript(GameTooltip, "OnTooltipSetUnit")
	
	if self.db.profile.inmenu then
		self:AddMenuItems()
	end
end

function Note:OnDisable()
	for i,t in pairs(self.friendbuttons) do
		t:Hide()
	end
	for i,t in pairs(self.ignorebuttons) do
		t:Hide()
	end
	for i,t in pairs(self.guildbuttons) do
		t:Hide()
	end
	self:RemoveMenuItems()
end

--/script AuldLangSyne:GetModule("Note"):AddMenuItems()
function Note:AddMenuItems()
	UnitPopupButtons["EDIT_NOTE"] = {text = L["Edit note"], dist = 0}
	self:SecureHook("UnitPopup_HideButtons", function()
		local dropdownMenu = UIDROPDOWNMENU_INIT_MENU -- _G[UIDROPDOWNMENU_INIT_MENU]
		for i,v in pairs(UnitPopupMenus[dropdownMenu.which]) do
			if v=="EDIT_NOTE" then UnitPopupShown[i] = (dropdownMenu.name == UnitName("player") and 0) or 1 end
		end
	end)
	self:SecureHook("UnitPopup_OnClick", function()
		local dropdownFrame = UIDROPDOWNMENU_INIT_MENU -- _G[UIDROPDOWNMENU_INIT_MENU]
		local button = this.value
		if button=="EDIT_NOTE" then self:ShowEditFrame(dropdownFrame.name, "generic") end
		PlaySound("UChatScrollButton")
	end)
	table.insert(UnitPopupMenus["PLAYER"], (#UnitPopupMenus["PLAYER"])-1, "EDIT_NOTE")
	table.insert(UnitPopupMenus["PARTY"], (#UnitPopupMenus["PARTY"])-1, "EDIT_NOTE")
end

local function remove_value(t, val)
	for i,v in pairs(t) do
		if v==val then
			table.remove(t, i)
			break
		end
	end
end
function Note:RemoveMenuItems()
	UnitPopupButtons["EDIT_NOTE"] = nil
	self:Unhook("UnitPopup_HideButtons")
	self:Unhook("UnitPopup_OnClick")
	remove_value(UnitPopupMenus["PLAYER"], "EDIT_NOTE")
	remove_value(UnitPopupMenus["PARTY"], "EDIT_NOTE")
end

function Note:HasNote(name)
	return self.db.realm.friend[name] or self.db.realm.ignore[name] or self.db.realm.guild[name] or self.db.realm.generic[name]
end

function Note:PrintNoteFor(name)
	if self.db.realm.friend[name] then
		self:Print(name .. " (friend): " .. self.db.realm.friend[name])
	end
	if self.db.realm.ignore[name] then
		self:Print(name .. " (ignore): " .. self.db.realm.ignore[name])
	end
	if self.db.realm.guild[name] then
		self:Print(name .. " (guild): " .. self.db.realm.guild[name])
	end
	if self.db.realm.generic[name] then
		self:Print(name .. ": " .. self.db.realm.generic[name])
	end
end

------------------------------------------------------------------------
-- Button display
------------------------------------------------------------------------

function Note:FriendsList_Update()
	if self.db.profile.onlogon then
		--This might be a friend-has-signed-on update.  So:
		--(Note that this is why we're not just using a bucket event or throttled event.)
		self:ListenFor("Login")
	end
	if FriendsFrame:IsVisible() and FriendsFrame.selectedTab == 1 then
		local friendOffset = FauxScrollFrame_GetOffset(FriendsFrameFriendsScrollFrame)
		local numFriends = GetNumFriends()
		local n = FRIENDS_TO_DISPLAY

		if numFriends < FRIENDS_TO_DISPLAY then n = numFriends end

		for i=1, n, 1 do
			local name, level, class, area, connected, status = GetFriendInfo(i + friendOffset)

			-- They need a note button.
			self.friendbuttons[i].name = name
			if self.db.realm.friend[name] == nil or self.db.realm.friend[name] == "" then
				self.friendbuttons[i]:GetNormalTexture():SetVertexColor(0.5,0.5,0.5)
			else
				self.friendbuttons[i]:GetNormalTexture():SetVertexColor(1,1,1)
			end
		end
	end
end

function Note:IgnoreList_Update()
	if FriendsFrame:IsVisible() and FriendsFrame.selectedTab == 1 then
		local ignoreOffset = FauxScrollFrame_GetOffset(FriendsFrameIgnoreScrollFrame)
		for i=1, IGNORES_TO_DISPLAY, 1 do
			local ignoreIndex = i + ignoreOffset
			local name = GetIgnoreName(ignoreIndex)
			self.ignorebuttons[i].name = name
			if self.db.realm.ignore[name] == nil or self.db.realm.ignore[name] == "" then
				self.ignorebuttons[i]:GetNormalTexture():SetVertexColor(0.5,0.5,0.5)
			else
				self.ignorebuttons[i]:GetNormalTexture():SetVertexColor(1,1,1)
			end
		end
	end
end

function Note:GuildStatus_Update()
	if FriendsFrame:IsVisible() and FriendsFrame.selectedTab == 3 then
		local guildOffset = FauxScrollFrame_GetOffset(GuildListScrollFrame)
		for i=1, GUILDMEMBERS_TO_DISPLAY, 1 do
			local guildIndex = i + guildOffset
			local name = GetGuildRosterInfo(guildIndex)
			self.guildbuttons[i].name = name
			if self.db.realm.guild[name] == nil or self.db.realm.guild[name] == "" then
				self.guildbuttons[i]:GetNormalTexture():SetVertexColor(0.5,0.5,0.5)
			else
				self.guildbuttons[i]:GetNormalTexture():SetVertexColor(1,1,1)
			end
		end
	end

end

------------------------------------------------------------------------
-- Who / logon note printing
------------------------------------------------------------------------

function Note:CHAT_MSG_SYSTEM(message)
	if self.listeningForLogin then
		local name = deformat(message, ERR_FRIEND_ONLINE_SS)
		if name and self.db.realm.friend[name] then
			--We schedule an event, so we can print the note _after_ the "[name] has come online" message.
			self:ScheduleEvent("AuldLangSynePrintNote", self.PrintNoteFor, 0.1, self, name)
		end
	end
	if self.listeningForWho then
		local name = deformat(message, WHO_LIST_FORMAT)
		if not name then name = deformat(message, WHO_LIST_GUILD_FORMAT) end
		if name and self.db.profile.onwho and self:HasNote(name) then
			self:ScheduleEvent("AuldLangSynePrintNote", self.PrintNoteFor, 0.1, self, name)
		end
	end
end

function Note:ListenFor(which, stop)
	if stop then
		self["listeningFor"..which] = false
		if self:IsEventRegistered("CHAT_MSG_SYSTEM") then
			self:UnregisterEvent("CHAT_MSG_SYSTEM")
		end
	else
		self["listeningFor"..which] = true
		self:RegisterEvent("CHAT_MSG_SYSTEM")
		self:ScheduleEvent(self.ListenFor, 1, self, which, true)
	end
end


------------------------------------------------------------------------
-- Tooltip notes
------------------------------------------------------------------------

function Note:OnTooltipSetUnit(tooltip, ...)
	if self.db.profile.intooltip then
		local name, unitid = tooltip:GetUnit()
		if UnitExists(unitid) then
			if self.db.realm.friend[name] then
				GameTooltip:AddLine(L["Friend: "] .. self.db.realm.friend[name], 0.5, 0.5, 0.5)
			end
			if self.db.realm.guild[name] then
				GameTooltip:AddLine(L["Guild: "] .. self.db.realm.guild[name], 0.5, 0.5, 0.5)
			end
			if self.db.realm.ignore[name] then
				GameTooltip:AddLine(L["Ignore: "] .. self.db.realm.ignore[name], 0.5, 0.5, 0.5)
			end
			if self.db.realm.generic[name] then
				GameTooltip:AddLine(L["Note: "] .. self.db.realm.generic[name], 0.5, 0.5, 0.5)
			end
		end
	end
	return self.hooks[tooltip].OnTooltipSetUnit(tooltip, ...)
end

------------------------------------------------------------------------
-- Importing from CTMod
------------------------------------------------------------------------

function Note:CTImport()
	--CTPlayerNotes is popular.  Ergo, we must steal its data.
	--NOTE: This will overwrite any existing notes.  This might be bad.

	local playerCount = 0
	local guildCount = 0
	local ignoreCount = 0
	local CtNoteTable
	
	if CT_CoreOptions then
		CtNoteTable = CT_CoreOptions["CHAR-" .. (UnitName("player")) .. "-" .. GetRealmName()]
	end
	if not CtNoteTable then
		self:Print(L["CT_Core is not loaded"])
		return
	end
	if CtNoteTable["friendNotes"] then
		for name, note in pairs(CtNoteTable["friendNotes"]) do
			if not self.db.realm.friend[name] then self.db.realm[name] = {} end
			self.db.realm.friend[name] = note
			playerCount = playerCount + 1
		end
	end
	if CtNoteTable["guildNotes"] then
		for name, note in pairs(CtNoteTable["guildNotes"]) do
			self.db.realm.guild[name] = note
			guildCount = guildCount + 1
		end
	end
	if CtNoteTable["ignoreNotes"]  then
		for name, note in pairs(CtNoteTable["ignoreNotes"]) do
			self.db.realm.ignore[name] = note
			ignoreCount = ignoreCount + 1
		end
	end

	self:Print(format(L["Imported %d player notes from CT_Core"], playerCount))
	self:Print(format(L["Imported %d guild notes from CT_Core"], guildCount))
	self:Print(format(L["Imported %d ignore notes from CT_Core"], ignoreCount))
end

------------------------------------------------------------------------
-- UI utility functions
------------------------------------------------------------------------

function Note:CreateButton(name, parent, point, relativePoint, x, y)
	local button = CreateFrame("Button", name, parent)
	button:SetWidth(16); button:SetHeight(16)

	--Left edge aligned to right edge of parent.
	button:SetPoint(point, parent, relativePoint, x, y)

	button:SetNormalTexture("Interface\\Buttons\\UI-GuildButton-PublicNote-Up")
	button:SetDisabledTexture("Interface\\Buttons\\UI-GuildButton-PublicNote-Disabled")
	button:SetHighlightTexture("Interface\\Buttons\\UI-GuildButton-PublicNote-Up")

	button:SetScript("OnClick", self.ButtonClick)
	button:SetScript("OnEnter", self.ButtonEnter)
	button:SetScript("OnLeave", self.ButtonLeave)

	return button
end

function Note.ButtonClick()
	Note:ShowEditFrame(this.name, this.type)
end

function Note.ButtonEnter()
	GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
	GameTooltip:ClearLines()
	GameTooltip:AddLine(L["Click to edit"], 1, 0.7, 0)
	GameTooltip:AddLine(Note.db.realm[this.type][this.name] or '', 0.6, 0.6, 0.6)
	GameTooltip:Show()
end

function Note.ButtonLeave()
	GameTooltip:Hide()
end

function Note:ShowEditFrame(name, notetype)
	if self.editFrame:IsVisible() and self.editFrame.name == name then
		self.editFrame.name = ""
		self.editFrame.type = ""
		self.editFrame:Hide()
	else
		self.editFrame.name = name
		self.editFrame.type = notetype
		self.editFrame.editbox:SetText(self.db.realm[notetype][name] or '')
		self.editFrame.edit_text:SetText(format(L["Editing note for %s"], name))
		self.editFrame:Show()
	end
end

function Note:CreateEditFrame()
	local f = CreateFrame("Frame", "AuldLangSyne_Edit", UIParent)
	f:SetFrameStrata("DIALOG")
	f:SetToplevel(true)
	f:SetWidth(300)
	f:SetHeight(100)
	f:SetPoint("CENTER", UIParent) --Center it on the screen.
	f:SetBackdrop({bgFile="Interface\\DialogFrame\\UI-DialogBox-Background", edgeFile="Interface\\DialogFrame\\UI-DialogBox-Border",
		tile=true, tileSize=32, edgeSize=32, insets={left=11, right=12, top=12, bottom=11}})

	local h = f:CreateTexture()
	h:SetTexture("Interface\\DialogFrame\\UI-DialogBox-Header")
	h:SetWidth(256)
	h:SetHeight(64)
	h:SetPoint("TOP", f, "TOP", 0, 12)

	local h_text = f:CreateFontString("AuldLangSyne_EditHeaderText", nil, "GameFontNormal")
	h_text:SetPoint("TOP", h, "TOP", 0, -14)
	h_text:SetText(L["Edit note"])

	local edit_text = f:CreateFontString("AuldLangSyne_EditDescText", nil, "GameFontNormal")
	edit_text:SetPoint("CENTER", f, "CENTER", 0, 20)
	--edit_text:SetText(

	--Somewhere to type.
	local edit = CreateFrame("EditBox", nil, f)
	edit:SetFontObject(ChatFontNormal)
	edit:SetHistoryLines(1); edit:SetMaxLetters(250); edit:SetTextInsets(10,10,0,0)
	edit:SetWidth(250); edit:SetHeight(32)
	edit:SetPoint("CENTER", f)
	edit:SetScript("OnShow", function() this:SetFocus() end)
	edit:SetScript("OnEnterPressed", function() self:SaveNote(self.editFrame.name, this:GetText(), self.editFrame.type) end)
	edit:SetScript("OnEscapePressed", function() this:SetText(""); this:GetParent():Hide(); end)

	--Textures for the editbox border.  ("BACKGROUND"?)
	local edit_l, edit_r, edit_b = edit:CreateTexture(), edit:CreateTexture(), edit:CreateTexture()
	edit_l:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Left"); edit_l:SetWidth(65); edit_l:SetHeight(32); edit_l:SetPoint("LEFT", edit, nil, -10); edit_l:SetTexCoord(0,0.2539,0,1);
	edit_r:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Right"); edit_r:SetWidth(25); edit_r:SetHeight(32); edit_r:SetPoint("RIGHT", edit, nil, 10); edit_r:SetTexCoord(0.9,1,0,1);
	edit_b:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Left"); edit_b:SetWidth(5); edit_b:SetHeight(32); edit_b:SetPoint("LEFT", edit_l, "RIGHT"); edit_b:SetPoint("RIGHT", edit_r, "LEFT"); edit_b:SetTexCoord(0.29296875,1,0,1);

	local b_accept = CreateFrame("Button", nil, f, "GameMenuButtonTemplate")
	b_accept:SetWidth(70); b_accept:SetHeight(21)
	b_accept:SetPoint("BOTTOM", f, "BOTTOM", -42, 12)
	b_accept:SetScript("OnClick", function() self:SaveNote(self.editFrame.name, this:GetParent().editbox:GetText(), self.editFrame.type) end)
	b_accept:SetText(L["Confirm"])

	local b_cancel = CreateFrame("Button", nil, f, "GameMenuButtonTemplate")
	b_cancel:SetWidth(70); b_cancel:SetHeight(21)
	b_cancel:SetPoint("BOTTOM", f, "BOTTOM", 42, 12)
	b_cancel:SetScript("OnClick", function() this:GetParent().editbox:SetText(""); this:GetParent():Hide(); end)
	b_cancel:SetText(L["Cancel"])

	f.editbox = edit
	f.edit_text = edit_text
	f:Hide()
	return f
end

function Note:SaveNote(name, note, ntype)
	--This puts a note into the db, and closes the edit frame if it"s open.
	if note == "" then note = nil end
	if not self.db.realm[ntype] then return end
	
	self.db.realm[ntype][name] = note
	if FriendsFrame:IsVisible() then
		if ntype == "friend" then
			FriendsList_Update()
		elseif ntype == "ignore" then
			IgnoreList_Update()
		elseif ntype == "guild" then
			GuildStatus_Update()
		end
	end

	if self.editFrame:IsVisible() then
		self.editFrame.editbox:SetText("")
		self.editFrame:Hide()
	end
end
